﻿using System;
using System.Collections;

namespace BizElements.Core
{
    /// <summary>Provides basic entity field validation, i.e. checks constraints defined in <see cref="IDbTable"/> object.</summary>
    public class EntityValidator : IEntityValidator
    {
        /// <summary>Gets or sets entity to validated.</summary>
        public virtual IEntity ValidatedEntity { get; set; }

        /// <summary>
        /// Validates all fields of an entity. Returns all encountered errors.
        /// </summary>
        /// <returns>Array of FieldError objects that describe all encountered errors. Empty array if there are no errors.</returns>
        public virtual FieldError[] ValidateAllFields()
        {
            ArrayList errorList = new ArrayList();
            object[] entityValues = this.ValidatedEntity.ToObjectArray();
            for (int fieldIdx = 0; fieldIdx < this.ValidatedEntity.Table.Columns.Count; fieldIdx++)
            {
                FieldError currError = new FieldError(this.ValidatedEntity.Table.Columns[fieldIdx], entityValues[fieldIdx]);
                if (currError.ErrorCode != FieldErrorCode.AllOk)
                    errorList.Add(currError);
            }

            FieldError[] errorArray = new FieldError[errorList.Count];
            for (int errIdx = 0; errIdx < errorArray.Length; errIdx++)
                errorArray[errIdx] = (FieldError)errorList[errIdx];

            return errorArray;
        }

        /// <summary>
        /// Returns a value which indicates whether all fields of an entity contain valid values. 
        /// </summary>
        /// <returns>True if all fields are valid; false otherwise.</returns>
        public virtual bool IsValidEntity()
        {
            object[] entityValues = this.ValidatedEntity.ToObjectArray();
            for (int fieldIdx = 0; fieldIdx < this.ValidatedEntity.Table.Columns.Count; fieldIdx++)
            {
                object currValue = entityValues[fieldIdx];
                IDbColumn currColumn = this.ValidatedEntity.Table.Columns[fieldIdx];
                FieldValidator fieldValidator = new FieldValidator(currColumn);
                FieldErrorCode errCode = fieldValidator.Validate(currValue);
                if (errCode != FieldErrorCode.AllOk)
                    return false;
            }

            return true;
        }

        /// <summary>
        /// Validates specified field.
        /// </summary>
        /// <remarks>Checks whether the value contained in the given field obeys basic constraints 
        /// (nullability, min/max value etc). Returns <see cref="FieldErrorCode"/> that indicates 
        /// the type of error encountered.</remarks>
        /// <param name="field">Field mapped in this entity.</param>
        /// <returns>Error code that indicates the type of encountered error.</returns>
        public virtual FieldErrorCode ValidateField(IDbColumn field)
        {
            // Note: this generic implementation has a lot of overhead.
            // It is recommended to implement optimized ValidateField method in inherited classes.

            if (this.ValidatedEntity.Table.Columns.GetByColumnName(field.ColumnName) == null)
                throw new ArgumentException(Messages.EntityModelBase_SpecFieldDoesntExistInEntity, "field");

            object fieldValue = this.ValidatedEntity.GetField(field);
            FieldError err = new FieldError(field, fieldValue);
            return err.ErrorCode;
        }

        /// <summary>
        /// Returns a value which indicates whether the specifified is valid.
        /// Checks whether the value contained in the given field obeys basic constraints (nullability, min/max value etc).
        /// </summary>
        /// <param name="field">Field mapped in this entity.</param>
        /// <returns>True if the field is valid; false otherwise.</returns>
        public virtual bool IsValidField(IDbColumn field)
        {
            FieldErrorCode error = ValidateField(field);
            if (error == FieldErrorCode.AllOk)
                return true;
            else
                return false;
        }
    }
}
